
package taller2.controller.modes.supervisor;

import java.awt.event.MouseEvent;
import java.util.ArrayList;

import javax.swing.JFrame;
import javax.swing.JOptionPane;

import taller2.controller.modes.Mode;
import taller2.interfaces.Connection;
import taller2.interfaces.Graph;
import taller2.interfaces.Node;
import taller2.utils.Point;
import taller2.utils.Constants;
import taller2.view.draws.DrawingArea;
import taller2.view.draws.GraphDrawer;

public class TopologicDfsSupervisorMode implements Mode {
	
	private Graph graph;

	private boolean finished;
	private ArrayList<Integer> solucion;
	private Node actual;
	
	private ArrayList<Node> visited;
	private ArrayList<Node> listaResultante;
	
	// constructor
	public TopologicDfsSupervisorMode(Graph graph){
		this.graph = graph;
		
		solucion = new ArrayList<Integer>();
		solucion = armarSolucion(graph);
		
		this.finished = false;
	}
	
	
	public void whenPressed(MouseEvent arg0, DrawingArea area) {
		GraphDrawer drawer = area.getDrawer();
		Point pos = new Point(arg0.getX(),arg0.getY());
		if(!finished)
			supervise(drawer,area,pos);
	}
	
	private void supervise(GraphDrawer drawer, DrawingArea area,Point pos){
		actual = drawer.getCloseByNode(pos);
		if(actual != null)
			processSolution();
		
		if(allNodesSelected()){
			JFrame frame = new JFrame();
			JOptionPane.showMessageDialog(frame,Constants.okMsg);

			finished = true;
		}
				
		selectConnections();
		area.repaint();
	}

	private void processSolution(){
		//verifico que sea valido, si lo es lo marco
		if (actual.getNumberId() == solucion.get(0)){
			solucion.remove(0);
			actual.select();
		//si no es valido aviso al usuario que se equivoco
		}else{
			JFrame frame = new JFrame();
			JOptionPane.showMessageDialog(frame,Constants.errorMsgTopoDFS);	
		}
	}		
	
	private ArrayList<Integer> armarSolucion(Graph graph){
		//ToDo considerar lista de soluciones vacia si es aciclico!!
		solveProblem(graph);
		
		//si al resolver dio lista vacia, pero el grafo no esta vacio es porque hay ciclos
		if ( (listaResultante.size() == 0) && (graph.getNodes().size() != 0) ){
			JFrame frame = new JFrame();
			JOptionPane.showMessageDialog(frame,"Recordar que el grafo debe ser aciclico!");	
		}
	
		ArrayList<Integer> sol;
		sol = new ArrayList<Integer>();
		
		for (Node n:listaResultante){
			sol.add(n.getNumberId());
		}
		
		return sol;
	}

	public void solveProblem(Graph g){

		graph = g;
		
		//el algoritmo es solo para grafos acíclicos!
		if (! esAciclico(graph)){
			return;
		}
			
		visited = new ArrayList<Node>();
		listaResultante = new ArrayList<Node>();

		ArrayList<Node> nodes = graph.getNodes();

		Node start;
		start = graph.getFirstNode();	

		for(int i=0; i<nodes.size() ; i++){
			Node node = nodes.get(i);
			node.setId(i);

		}

        //mientras no esten todos visitados
		while(visited.size() != graph.getNodes().size()){

			visitar(start);
			
			//y busco nuevo nodo inicial
			for(Node n: nodes){
				if  (! visited.contains(n)) {
					start = n;
					break;
				}
			}
		}
		
	}
	
	private void visitar(Node node){
		if ( ! visited.contains(node) ){	
			for (Connection connection : node.getConnections()) {
				Node other = connection.getOtherNode();
				visitar(other);
			}
			visited.add(node);
			
			listaResultante.add(0, node);
		}
		
	}
	
	private boolean esAciclico(Graph g){
		
		boolean encontrado = false;
						
		for(Node start : g.getNodes()){
								
			encontrado = findCyclesStartingWith(start,start,g);
			if (encontrado)
				break;
		}
			
		if (encontrado)
			 //se encontro un ciclo -> no es aciclico
			return false;
		else
			return true;
	}
	
	private boolean findCyclesStartingWith(Node start,Node act,Graph g){
		
		if(!act.isSelected()){
			act.mark();
		}
		
		for(Connection connection : act.getConnections()){
			Node otherNode = connection.getOtherNode();
			
			boolean areEqual = otherNode.equals(start);

			if(areEqual){
				return true;				
			}

			boolean unused = !otherNode.isMarked() && !otherNode.isSelected();
			if(unused){
				if(findCyclesStartingWith(start,otherNode,g)){
					return true;							
				}
			}
		}
		return false;
	}
	
	private void selectConnections(){
		for(Connection conn : graph.getAllConnections().values()){
			Node node = conn.getOtherNode();
			Node node2 = conn.getThisNode();
			if(node.isSelected() && node2.isSelected())
				conn.select();
		}
	}
	
	private boolean allNodesSelected() {
		for(Node node : graph.getNodes()){
			if(!node.isSelected())
				return false;
		}
		return true;
	}
	
	public void whenReleased(MouseEvent arg0, DrawingArea area) {}

	public void whenDragged(MouseEvent arg0, DrawingArea area) {}

}
